home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Graphics / Plotting / aa_Intel_Only / Gnuplot / GnuplotSource / PlotView.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  13.9 KB  |  661 lines

  1. /*
  2.  *  Copyright (C) 1993  Robert Davis
  3.  *
  4.  *  This program is free software; you can redistribute it and/or
  5.  *  modify it under the terms of Version 2, or any later version, of 
  6.  *  the GNU General Public License as published by the Free Software 
  7.  *  Foundation.
  8.  */
  9.  
  10. static char RCSId[]="$Id: PlotView.m,v 1.15 1993/05/30 10:01:55 davis Exp $";
  11.  
  12.  
  13. #import <appkit/Application.h>
  14. #import <appkit/ClipView.h>
  15. #import <appkit/Font.h>
  16. #import <appkit/FontManager.h>
  17. #import <appkit/Matrix.h>
  18. #import <appkit/MenuCell.h>
  19. #import <appkit/NXImage.h>
  20. #import <appkit/Pasteboard.h>
  21. #import <appkit/SavePanel.h>
  22. #import <appkit/Text.h>
  23.  
  24. #import <defaults/defaults.h>    /* NXGetTempFilename()    */
  25.  
  26. #import <dpsclient/psops.h>
  27. #import <libc.h>        /* unlink(), MAXPATHLEN    */
  28.  
  29. #import "GnuplotPlot.h"
  30. #import "PlotView.h"
  31.  
  32.  
  33. @interface PlotView (Private)
  34.  
  35. - (const char *) _writeEPSToTempFile;
  36. - _unlinkTempFile;
  37.  
  38. @end
  39.  
  40.  
  41.  
  42. /*  Does the list of NXAtoms "types" include the NXAtom "type"?  */
  43. static BOOL includesType(NXAtom *types, NXAtom type)
  44. {
  45.     while (types && *types) {
  46.         if (type == *types) return YES;
  47.         types++;
  48.     }
  49.     return NO;
  50. }
  51.  
  52.  
  53.  
  54. @implementation PlotView
  55.  
  56.  
  57. - initFrame:(const NXRect *)frameRect;
  58. {
  59.     const char *const sendTypes[] = {NXPostScriptPboardType, NXTIFFPboardType,
  60.                      NULL};
  61.  
  62.     [super initFrame:frameRect];
  63.     [self setClipping: NO];    /* Because we're in a ClipView already    */
  64.     [self drawInSuperview];    /* Use our ClipView's coordinate system    */
  65.  
  66.     fullPath = NULL;
  67.     epsStream = NULL;
  68.     tempFileNeedsUpdate = YES;
  69.  
  70.     /* Setup services */
  71.     [NXApp registerServicesMenuSendTypes: sendTypes andReturnTypes: NULL];
  72.  
  73.     return self;
  74. }
  75.  
  76.  
  77.  
  78. /*  
  79.  *  This method, in addition to freeing the image, unlinks the 
  80.  *  temporary EPS file if it exists.
  81.  */
  82. - free
  83. {
  84.     [self _unlinkTempFile];
  85.  
  86.     if (image)
  87.     [image free];
  88.  
  89.     return [super free];
  90. }
  91.  
  92.  
  93. - (BOOL) acceptsFirstResponder
  94. {
  95.     return YES;            /* So the font can be changed with fontpanel */
  96. }
  97.  
  98.  
  99. - (BOOL)acceptsFirstMouse
  100. {
  101.     return YES;            /* So we can drag the plot to other apps */
  102. }
  103.  
  104.  
  105.  
  106. /*
  107.  *  Redisplays the contents of the view by compositing the image onto 
  108.  *  the view.
  109.  */
  110. - drawSelf:(NXRect *)r :(int) count
  111. {
  112.     PSsetgray(NX_WHITE);    /* For color machines, we must clear    */
  113.     NXRectFill(&(r[0]));    /* the window to white.            */
  114.  
  115.     if (image)
  116.     [image composite:NX_SOVER fromRect:&(r[0]) toPoint:&(r[0].origin)];
  117.  
  118.     return self;
  119. }
  120.  
  121.  
  122.  
  123. - mouseDown:(NXEvent *)theEvent
  124. {
  125.     if (image)  {
  126.  
  127.     int    oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK];
  128.     BOOL    loop = YES;
  129.     NXEvent    mouseDownEvent = *theEvent;
  130.     NXPoint    downLoc = mouseDownEvent.location;
  131.  
  132.     while (loop) {
  133.         NXEvent    *nextEvent;
  134.         NXPoint    p;
  135.  
  136.         nextEvent = [NXApp getNextEvent:(NX_MOUSEUPMASK |
  137.                          NX_MOUSEDRAGGEDMASK)];
  138.         p = nextEvent->location;
  139.  
  140.         switch (nextEvent->type) {
  141.         case NX_MOUSEDRAGGED:
  142.  
  143.         if ((fabs(downLoc.x - p.x) > 5) ||    /* Hysteresis */
  144.             (fabs(downLoc.y - p.y) > 5)) {
  145.  
  146.             NXPoint    zero = {0.0, 0.0};
  147.             const char *const types[] = {NXPostScriptPboardType,
  148.                          NXTIFFPboardType,
  149.                          NXFilenamePboardType, NULL};
  150.             id        pboard = [Pasteboard newName:NXDragPboard];
  151.  
  152.             [self copyToPasteboard:pboard types:(NXAtom *)&types];
  153.  
  154.             /*  
  155.              *  Here's a neat trick:  if the user presses the 
  156.              *  alternate key during mouse down, the file will 
  157.              *  be dragged out of the view instead of the 
  158.              *  image and all its EPS, TIFF, etc. info.
  159.              */
  160.             if ((&mouseDownEvent)->flags & NX_ALTERNATEMASK) {
  161.  
  162.             NXRect fileRect = {{(&mouseDownEvent)->location.x-24.0,
  163.                        (&mouseDownEvent)->location.y-24.0},
  164.                         {48.0, 48.0}};
  165.             [[[window contentView] superview]
  166.                      convertRect:&fileRect toView:self];
  167.  
  168.             [self dragFile:fullPath
  169.                   fromRect:&fileRect
  170.                  slideBack:YES
  171.                  event:&mouseDownEvent];
  172.  
  173.             } else {
  174.  
  175.             [self dragImage:image
  176.                      at:&zero
  177.                  offset:&zero
  178.                       event:&mouseDownEvent
  179.                  pasteboard:pboard
  180.                      source:self
  181.                   slideBack:YES];
  182.  
  183.             }
  184.  
  185.             loop = NO;
  186.         }
  187.         break;
  188.  
  189.         case NX_MOUSEUP:
  190.         loop = NO;
  191.  
  192.         }
  193.     }
  194.  
  195.     [window setEventMask:oldMask];
  196.     return self;
  197.  
  198.     } else    /* no image to drag */
  199.  
  200.     return nil;
  201.  
  202. }
  203.  
  204.  
  205. - (NXDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag
  206. {
  207.     return (NX_DragOperationCopy | NX_DragOperationGeneric);
  208. }
  209.  
  210.  
  211.  
  212. - (BOOL)shouldDelayWindowOrderingForEvent:(NXEvent *)theEvent
  213. {
  214.     return YES;
  215. }
  216.  
  217.  
  218.         
  219. /*
  220.  *  This method takes only one scale factor because we never want the 
  221.  *  view to have different proportions than the image.  Scale factor 
  222.  *  of zero means size to window (so that the entire plot is visible 
  223.  *  in the window).  Returns the actual factor that the view was 
  224.  *  scaled.
  225.  */
  226. - (float)scale:(float)scaleFactor
  227. {
  228.     float    factor;
  229.     NXSize    imageSize;
  230.     NXRect    ourRect;
  231.  
  232.     if (image)  {
  233.  
  234.     if (scaleFactor)  {
  235.  
  236.         factor = scaleFactor;
  237.  
  238.     } else  {
  239.  
  240.         [superview getDocVisibleRect: &ourRect];
  241.         factor = ourRect.size.width / imageOrigWidth;
  242.         if ((imageOrigHeight * factor) > ourRect.size.height)
  243.         factor = ourRect.size.height / imageOrigHeight;
  244.  
  245.     }
  246.  
  247.     imageSize.width = imageOrigWidth * factor;
  248.     imageSize.height = imageOrigHeight * factor;
  249.     [image setSize:&imageSize];
  250.  
  251.     /* Show the newly sized image. */
  252.  
  253.     NX_WIDTH(&bounds) = NX_WIDTH(&frame) = imageSize.width;
  254.     NX_HEIGHT(&bounds) = NX_HEIGHT(&frame) = imageSize.height;
  255.     [superview descendantFrameChanged:self];
  256.  
  257.     [self display];
  258.     return factor;
  259.  
  260.     } else
  261.     return 0.0;
  262. }
  263.  
  264.  
  265.  
  266.  
  267. - (BOOL)validateCommand:menuCell
  268. {
  269.     SEL action = [menuCell action];
  270.  
  271.     if (action == @selector(copy:))
  272.         return image? YES :NO;
  273.     else if (action == @selector(copyFont:)) {
  274.     [[FontManager new] setEnabled:image? YES : NO];
  275.         return image? YES :NO;
  276.     } else if (action == @selector(pasteFont:))
  277.         return image? YES :NO;
  278.  
  279.     return YES;
  280. }
  281.  
  282.  
  283.  
  284. - changeFont:sender
  285. {
  286.     id newfont;
  287.     id doc;
  288.  
  289.     newfont = [sender convertFont: [(doc = [window delegate]) currentFont]];
  290.     [doc setCurrentFont:newfont];
  291.  
  292.     return self;
  293. }
  294.  
  295.  
  296. - copyFont:sender
  297. {
  298.     /* 
  299.      *  This method is invoked when a user presses the "Copy Font" 
  300.      *  menu button.  We're supposed to put the current font on the 
  301.      *  font pasteboard, and the easiest way to do that is to take 
  302.      *  advantage of the Text class's ability to do that.  Every step 
  303.      *  here is necessary:  we create a Text object, set it to RTF 
  304.      *  (non-monofont), make a selection (even though there is no text 
  305.      *  to select, there must be a selection), set the font to the 
  306.      *  doc's current font, tell the text object to copy the font to 
  307.      *  the font pasteboard, and then free the text object.
  308.      */
  309.     [[[[[[[Text allocFromZone: [self zone]] init]
  310.           setMonoFont: NO]
  311.                setSel: 0:1]
  312.               setFont: [[window delegate] currentFont]]
  313.              copyFont: sender]
  314.                  free];
  315.  
  316.     return self;
  317. }
  318.  
  319.  
  320. - pasteFont:sender
  321. {
  322.     Text *text = [[Text allocFromZone: [self zone]] init];
  323.  
  324.     /* 
  325.      *  We must specify that the text is not monofont (i.e. is RTF) 
  326.      *  and there must be a selection in order to paste.  There 
  327.      *  doesn't need to be any text.
  328.      */
  329.     [[text setMonoFont:NO] setSel:0 :1];
  330.  
  331.     if ([text pasteFont:sender])    /* Returns nil if there's trouble */
  332.     [[window delegate] setCurrentFont:[text font]];
  333.  
  334.     [text free];
  335.     return self;
  336. }
  337.  
  338.  
  339.  
  340. - validRequestorForSendType:(NXAtom)sendType andReturnType:(NXAtom)returnType
  341. /*
  342.  *  Services menu support.
  343.  *  We are a valid requestor if the send type is EPS and there is no 
  344.  *  return data from the request.
  345.  */
  346. {
  347.     if (image &&
  348.     ((sendType == NXPostScriptPboardType) ||
  349.      (sendType == NXTIFFPboardType)) &&
  350.     (!returnType || !*returnType))
  351.  
  352.     return self;
  353.  
  354.     return [superview validRequestorForSendType:sendType
  355.                   andReturnType:returnType];
  356. }
  357.  
  358.  
  359.  
  360. - (BOOL) writeSelectionToPasteboard:pboard types:(NXAtom *)types
  361. /*
  362.  *  Services menu support.
  363.  *  Here we are asked by the Services menu mechanism to supply
  364.  *  the image data (which we said we were a valid requestor for
  365.  *  in the above method).
  366.  */
  367. {
  368.     while (types && *types) {
  369.         if ((*types == NXPostScriptPboardType) || (*types == NXTIFFPboardType))
  370.         break;
  371.         types++;
  372.     }
  373.  
  374.     if ([self copyToPasteboard:pboard types:types]) {
  375.         return YES;
  376.     } else {
  377.         return NO;
  378.     }
  379. }
  380.  
  381.  
  382. - copy:sender
  383. {
  384.     return [self copyToPasteboard:[Pasteboard new]];
  385. }
  386.  
  387.  
  388.  
  389. /*  
  390.  *  A generic copyToPasteboard: copies our contents in all possible 
  391.  *  representations.
  392.  */
  393. - copyToPasteboard:pboard
  394. {
  395.     NXAtom types[] = {NXPostScriptPboardType,
  396.               NXTIFFPboardType,
  397.               NXFilenamePboardType, NULL};
  398.  
  399.     return [self copyToPasteboard:pboard types:types];
  400. }
  401.     
  402.  
  403.  
  404.  
  405. - copyToPasteboard:pboard types:(NXAtom *)typesList
  406. {
  407.     if (image && typesList && *typesList) {
  408.  
  409.     NXStream *stream;   
  410.     const char *types[4];
  411.     int count;
  412.  
  413.     count = 0;
  414.     if (includesType (typesList, NXPostScriptPboardType))
  415.         types[count++] = NXPostScriptPboardType;
  416.     if (includesType (typesList, NXTIFFPboardType))
  417.         types[count++] = NXTIFFPboardType;
  418.     if (includesType (typesList, NXFilenamePboardType))
  419.         types[count++] = NXFilenamePboardType;
  420.     types[count] = NULL;
  421.  
  422.     if (count)
  423.         [pboard declareTypes:types num:count owner:[self class]];
  424.     else {
  425.         types[0] = NXPostScriptPboardType;
  426.         types[1] = NULL;
  427.         [pboard declareTypes:types num:1 owner:[self class]];
  428.     }
  429.  
  430.     if (includesType(types, NXPostScriptPboardType))  {
  431.         stream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  432.         [self copyPSToStream:stream];
  433.         [pboard writeType:NXPostScriptPboardType fromStream:stream];
  434.         NXCloseMemory(stream, NX_FREEBUFFER);
  435.     }
  436.  
  437.     if (includesType(types, NXTIFFPboardType)) { 
  438.         stream = NXOpenMemory(NULL, 0, NX_READWRITE);
  439.         [self copyTIFFToStream:stream];
  440.         [pboard writeType:NXTIFFPboardType fromStream:stream];
  441.         NXCloseMemory(stream, NX_FREEBUFFER);
  442.     } 
  443.  
  444.     if (includesType(types, NXFilenamePboardType)) {
  445.         const char *path = [self _writeEPSToTempFile];
  446.  
  447.         if (path)
  448.         [pboard writeType:NXFilenamePboardType data:path
  449.                                                     length:strlen(path)];
  450.     }
  451.  
  452.     return self;
  453.  
  454.     } else
  455.  
  456.     return nil;
  457. }
  458.  
  459.  
  460. - copyPSToStream:(NXStream *)stream
  461. {
  462.  
  463.     if (epsStream) {
  464.  
  465.     char *streambuf;
  466.     int len, maxlen;
  467.  
  468.     NXGetMemoryBuffer(epsStream, &streambuf, &len, &maxlen);
  469.     NXPrintf (stream, "%s", streambuf);
  470.     NXFlush (stream);
  471.  
  472.     return self;
  473.  
  474.     }
  475.  
  476.     return nil;
  477. }
  478.  
  479.  
  480.  
  481. - copyTIFFToStream:(NXStream *)stream
  482. {
  483.     if (image && stream) {
  484.     [image writeTIFF:stream];
  485.     return self;
  486.     }
  487.  
  488.     return nil;
  489. }
  490.  
  491.  
  492.  
  493. - newStream:(NXStream *) aStream
  494. {
  495.     NXSize imageSize;
  496.  
  497.     if (image)
  498.     [image free];
  499.  
  500.     if (aStream)  {
  501.  
  502.     NXSeek(aStream, 0, NX_FROMSTART);
  503.     image = [[[[NXImage allocFromZone: [self zone]]
  504.                initFromStream: aStream]
  505.                   setScalable: YES]
  506.               setDataRetained: YES];
  507.  
  508.     [image getSize:&imageSize];
  509.     imageOrigWidth = imageSize.width;
  510.     imageOrigHeight = imageSize.height;
  511.  
  512.     NX_WIDTH(&bounds) = NX_WIDTH(&frame) = imageSize.width;
  513.     NX_HEIGHT(&bounds) = NX_HEIGHT(&frame) = imageSize.height;
  514.     epsStream = aStream;
  515.  
  516.     } else {
  517.     NX_WIDTH(&bounds) = NX_HEIGHT(&bounds) = NX_WIDTH(&frame) =
  518.         NX_HEIGHT(&frame) = 0;
  519.     imageOrigWidth = imageOrigHeight = 0;
  520.     image = nil;
  521.     epsStream = NULL;
  522.     }
  523.  
  524.     tempFileNeedsUpdate = YES;
  525.  
  526.     return self;
  527. }
  528.  
  529.  
  530.  
  531.  
  532.  
  533. - (BOOL)saveEPSToFile:(const char *)filename
  534. {
  535.     /*  
  536.      *  Saves the epsStream to a file as Encapsulated PostScript.  
  537.      *  Assumes filename is a validly formed, unique pathname.  (It 
  538.      *  will overwrite the file if filename is not unique.)
  539.      */
  540.     
  541.     return !NXSaveToFile (epsStream, filename);
  542. }
  543.  
  544.  
  545.  
  546. - (BOOL)saveTIFFToFile:(const char *)filename
  547. {
  548.     /*  
  549.      *  Saves the epsStream to a file as TIFF.
  550.      *  Assumes filename is a validly formed, unique pathname.  (It 
  551.      *  will overwrite the file if filename is not unique.)
  552.      */
  553.     
  554.     NXStream *newStream;
  555.     
  556.     if (newStream = NXOpenMemory (NULL, 0, NX_WRITEONLY)) {
  557.  
  558.     BOOL returnval = YES;
  559.  
  560.     [self copyTIFFToStream:newStream];
  561.     if (NXSaveToFile (newStream, filename))
  562.         returnval = NO;
  563.     NXCloseMemory (newStream, NX_FREEBUFFER);
  564.  
  565.     return returnval;
  566.     }
  567.  
  568.     return NO;
  569. }
  570.  
  571.  
  572.  
  573. // Shuts up the compiler about unused RCSId
  574. - (const char *) rcsid
  575. {
  576.     return RCSId;
  577. }
  578.  
  579.  
  580. @end
  581.  
  582.  
  583.  
  584. @implementation PlotView (Private)
  585.  
  586. - _unlinkTempFile
  587. {
  588.     if (fullPath)  {
  589.     if (*fullPath)
  590.         unlink (fullPath);
  591.     NXZoneFree ([self zone], fullPath);
  592.     fullPath = NULL;
  593.     }
  594.  
  595.     return self;
  596. }
  597.  
  598.  
  599.  
  600. - (const char *) _writeEPSToTempFile
  601. {
  602.     const char *realName;
  603.     char *cur;
  604.     int pos;
  605.  
  606.     /*  
  607.      *  If epsStream has been written to a temp file before, and the 
  608.      *  epsStream hasn't changed since then, we don't need to write 
  609.      *  the temp file again.  This checks to see if the stream has 
  610.      *  changed.  It also should (but doesn't) check to see if the 
  611.      *  temp file still exists and is accessable (readable).
  612.      */
  613.     if (!tempFileNeedsUpdate)
  614.     return fullPath;
  615.     else if (!epsStream) {
  616.     [self _unlinkTempFile];
  617.     return NULL;
  618.     }
  619.  
  620.     [self _unlinkTempFile];    /* Get rid of previous temp file, if any */
  621.     fullPath = (char *) NXZoneMalloc ([self zone], MAXPATHLEN * sizeof (char));
  622.     *fullPath = '\0';
  623.  
  624.     /*  
  625.      *  We need to come up with a unique name for the temp file, which 
  626.      *  will reside in /tmp.  We use the name of the plot and change 
  627.      *  it with NXGetTempFilename.
  628.      */
  629.     realName = [[window delegate] name];
  630.     if (cur = rindex(realName, '/')) {    /* The plot has been titled    */
  631.  
  632.     if (++cur)
  633.         sprintf (fullPath, "/tmp/%s", cur);
  634.     else
  635.         return NULL;        /* ...ERROR: path ends in slash    */
  636.  
  637.     } else                /* The plot is untitled        */
  638.  
  639.     sprintf (fullPath, "/tmp/%s", realName);
  640.  
  641.  
  642.     pos = strlen (fullPath);
  643.  
  644.     if (cur = rindex (fullPath, '.')) {    /* The name has an extension    */
  645.     pos -= strlen (cur);        /* Replace it with .eps        */
  646.     strcpy (cur, "xxxxxx.eps");
  647.     } else                /* The name has no extension    */
  648.     strcat (fullPath, "xxxxxx.eps");
  649.  
  650.  
  651.     NXGetTempFilename(fullPath, pos);    /* Make sure filename is unique    */
  652.     [self saveEPSToFile:fullPath];    /* Save the stream to the file    */
  653.  
  654.     tempFileNeedsUpdate = NO;
  655.     return fullPath;
  656. }
  657.  
  658.  
  659.  
  660. @end
  661.